module syncfifo #(
				  parameter CNT_WIDTH = 4,		//the depth is 2^4=16
							DATA_WIDTH = 8
				  ) (
					 input reset, clk,
					 input read, write,
					 input [DATA_WIDTH-1:0] data_in,
					 output reg [DATA_WIDTH-1:0] data_out,
					 output empty, full, almost_empty, almost_full);

reg 	  [DATA_WIDTH-1:0] memory[(1<<CNT_WIDTH)-1:0]; //total 2^CNT_WIDTH
reg 	  [CNT_WIDTH-1:0] rptr, wptr;
reg 	  [CNT_WIDTH:0] count;					//the actual width is (CNT_WIDTH+1)

wire 			  valid_read, valid_write;

always@(posedge clk)
begin
  if(valid_read)
	data_out <= memory[rptr];
  else
	data_out <= {DATA_WIDTH{1'bx}};	//needed ? 

  if(valid_write)
	memory[wptr] <= data_in;
end

//read pointer
always@(posedge reset or posedge clk)
begin
  if(reset)
	rptr <= 0;
  else 
	// if(read & ~empty)
	if(valid_read)
	  rptr <= rptr + 1;			//point to next
end

//write pointer
always@(posedge reset or posedge clk)
begin
  if(reset)
	wptr <= 0;
  else 
	// if(write & ~full)
	if(valid_write)
	  wptr <= wptr + 1;			//point to next 
end

//the counter of words in fifo
always@(posedge reset or posedge clk)
begin
  if(reset)
	count <= 0;
  else
  begin
	if(valid_read ^ valid_write)
	  if(valid_read)
		count <= count - 1;
	  else
		count <= count + 1;
	// if(read^write)
	//   if(read & ~empty)
	// 	count <= count - 1;
	//   else if(~full)
	// 	count <= count + 1;
  end
end

assign 			  empty 	  = (count == 0);
assign 			  almost_empty = (count == 1);
assign 			  full 		  = (count == {1'b1, {CNT_WIDTH{1'b0}}});
assign 			  almost_full = (count == {1'b0, {CNT_WIDTH{1'b1}}});

assign 			  valid_read  = read & ~empty;
assign 			  valid_write = write & ~full;

endmodule // syncfifo


module test();

reg 			  reset, clk;
reg 			  read, write;
reg 	  [7:0]   din;
wire 	  [7:0]   dout;
wire 			  empty, full, almost_empty, almost_full;

syncfifo #(
	.CNT_WIDTH				(4), 
	.DATA_WIDTH				(8)
	) mysyncfifo ( 
				   .reset	(reset), 
				   .clk		(clk),
				   .read	(read),
				   .write	(write),
				   .data_in	(din),
				   .data_out(dout),
				   .empty 	(empty),
				   .full	(full),
				   .almost_empty (almost_empty),
				   .almost_full (almost_full) );

initial begin
clk = 0;
forever #5 clk = ~clk;  
end

initial
begin
  reset = 0;
  #10 reset = 1;
  #20 reset = 0;
  #10 read = 0; write = 0;

  #10 write = 1;
  repeat(20)
  begin
	#10 din = #1 $random%256;
  end

  #20 read = 1; write = 0;
  #100 read = 0;
  #10 read = 1;
  #100 read = 0;
  
end
endmodule
